CloudFormationを使ってAmazon RDSのインスタンスをスナップショットから立ち上げてみました
初めに
Amazon RDSはスナップショットの復元は別アカウントからのデータ移行や、バックアップからの復元、様々なケースで利用されます。
個人的にはバックアップからの復元や既存ののデータを利用した一時検証環境などを使うケースで利用することが多く、その関係IaCで管理する環境に含めることが少なく今まで手動で立ち上げしかやったことのない対応でした。
直近別環境から既存RDSのスナップショットを利用して新規環境を立ち上げる対応でスナップショットから立ち上げる対応をCloudFormatinoで書く機会があったので備忘録として残しておきます。
別アカウントからのスナップショット復元
本筋からは少し外れますが別のアカウントからのスナップショットを利用する場合は、そのアカウントからスナップショットを共有してもらう必要があります。
なお自動バックアップでは共有できないようなので共有が必要な場合は自動バックアップとは別に手動でスナップショットを取得し、そちらを共有する必要があります。
その他詳細は上記AWSドキュメントページをご参照ください。
スナップショットからの立ち上げ方
今回は非Aurora環境から試しますがこの場合はAWS::RDS::DBInstanceのDBSnapshotIdentifier
に対してスナップショットのARNを指定することでそのスナップショットからのインスタンスの立ち上げが可能です。
Auroraを利用する場合はAWS::RDS::DBClusterのSnapshotIdentifier
となりそうですが、DBInstanceの方にもDBClusterSnapshotIdentifier
というパラメータがあるのでこの辺りの指定は改めて機会がありましたら確認してみます。
DBSnapshotIdentifierの指定変更に関する注意事項
AWS::RDS::DBInstanceに記載がありますが一度指定した後の変更には注意が必要です。
After you restore a DB instance with a DBSnapshotIdentifier property, you can delete the DBSnapshotIdentifier property. When you specify this property for an update, the DB instance is not restored from the DB snapshot again, and the data in the database is not changed. However, if you don't specify the DBSnapshotIdentifier property, an empty DB instance is created, and the original DB instance is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB instance is restored from the specified DBSnapshotIdentifier property, and the original DB instance is deleted.
つまりのところDBSnapshotIdentifier
でしているする復元元のスナップショットの識別子(ARN)の値が指定された場合は(そのスナップショットで再作成が必要なため)置換処理がかかるようです。
ただし指定がある状態から指定を削除した場合は特に何も処理が行われない形になるとのことです(現状維持)。
削除保護をつけておくことで防止はできますが誤って指定を変えてしまった場合に元のDBを吹き飛ばす可能性があるので一応注意しましょう。
テンプレート
CloudFormationテンプレートは以下のようになります。
今回はDBSnapshotArn
を空とすることでDBSnapshotIdentifier
がAWS::NoValue
(すなわちパラメータ無し)として扱われるようになっております。
今回の対応は以下の流れで対応を行います。
CloudFormationで新規立ち上げ->DB内容変更->スナップショット作成->DB内容変更->スナップショット指定追加(スナップショット復元)->スナップショット指定削除(空のDB作成)
VPC等一部のリソースは毎回作成するのは煩雑でベースになるものを事前にあるのでそちらを利用しています。バージョンのベタ書き等がありますがその点は目を瞑っていただければと思います。
AWSTemplateFormatVersion: 2010-09-09 Parameters: Env: Type: String Prefix: Type: String DeleteProtection: Type: String AllowedValues: - True - False EnableMultiAZ: Type: String AllowedValues: - True - False VPCId: Type: String RDSSGId: Type: String SubnetGroupId: Type: String DBSnapshotId: Type: String DBInstanceClass: Type: String RDSStorageSize: Type: Number Conditions: SpecifySnapshot: !Not [ !Equals [ !Ref DBSnapshotId, ""]] Resources: #---------------------------------- #----- RDS Instance #---------------------------------- DBInstance: Type: AWS::RDS::DBInstance Properties: Engine: postgres MasterUsername: postgres AllocatedStorage: !Ref RDSStorageSize StorageType: gp3 # 後述しますがこの設定が原因で後々失敗します DBInstanceIdentifier: !Sub ${Env}-${Prefix} MasterUserPassword: !Sub "{{resolve:secretsmanager:${RDSMasterUserPassword}:SecretString:password::}}" AvailabilityZone: !Sub ${AWS::Region}a DBInstanceClass: !Ref DBInstanceClass AutoMinorVersionUpgrade: False EnablePerformanceInsights: True MultiAZ: !Ref EnableMultiAZ CACertificateIdentifier: "rds-ca-rsa4096-g1" CopyTagsToSnapshot: True DBSubnetGroupName: !Ref SubnetGroupId OptionGroupName: !Ref RDSOptionGroup DBSnapshotIdentifier: Fn::If: - SpecifySnapshot - !Ref DBSnapshotId - !Ref AWS::NoValue VPCSecurityGroups: - !Ref RDSSGId DBParameterGroupName: !Ref InstanceParameterGroup DeletionProtection: !Ref DeleteProtection Tags: - Key: Environment Value: !Ref Env RDSMasterUserPassword: Type: AWS::SecretsManager::Secret Properties: Name: !Sub ${Env}-${Prefix}-master-password GenerateSecretString: GenerateStringKey: password PasswordLength: 20 ExcludePunctuation: True SecretStringTemplate: '{}' #---------------------------------- #----- Parameter Group #---------------------------------- InstanceParameterGroup: Type: AWS::RDS::DBParameterGroup Properties: DBParameterGroupName: !Sub ${Env}-${Prefix}-instance-pg Description: "-" Family: postgres15 Tags: - Key: Environment Value: !Ref Env #---------------------------------- #----- Option Group #---------------------------------- RDSOptionGroup: Type: AWS::RDS::OptionGroup Properties: EngineName: postgres MajorEngineVersion: 15 OptionGroupName: !Sub ${Env}-${Prefix}-og OptionGroupDescription: "-" Tags: - Key: Environment Value: !Ref Env
新規立ち上げ〜データ投入〜スナップショット作成
まずはスナップショットなしで立ち上げを行います。
先ほどのテンプレートでスタックを立ち上げる際にDBSnapshotId
を空、後々再作成を行う関係でDeleteProtection
をfalseにして削除保護をつけないようにして起動します。
起動後接続を行い適当なテストデータを作っておきます。
postgres=> select version(); version --------------------------------------------------------------------------------------------------------- PostgreSQL 15.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-12), 64-bit (1 row) postgres=> CREATE TABLE history(id serial, text text, create_date timestamp DEFAULT now()); CREATE TABLE postgres=> INSERT INTO history(text) SELECT md5(generate_series::text) FROM generate_series(1,20); INSERT 0 20 postgres=> SELECT * FROM history; id | text | create_date ----+----------------------------------+---------------------------- 1 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:41:54.345255 2 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:41:54.345255 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:41:54.345255 4 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:41:54.345255 5 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:41:54.345255 6 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:41:54.345255 7 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:41:54.345255 8 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:41:54.345255 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:41:54.345255 10 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:41:54.345255 11 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:41:54.345255 12 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:41:54.345255 13 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:41:54.345255 14 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:41:54.345255 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:41:54.345255 16 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:41:54.345255 17 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:41:54.345255 18 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:41:54.345255 19 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:41:54.345255 20 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:41:54.345255 (20 rows)
マネジメントコンソールから手動でスナップショットを取得します。
後ほどCloudFormationのパラメータのDBSnapshotId
(と言いつつARN)を入れるのでスナップショットのARNを控えておきます。
今回は同一アカウントのため前述のような別アカウント共有は行いません。
データ変更〜スナップショット復元
上記のスナップショットで戻したことが確認できるようにさらにデータを追加しておきます。
postgres=> INSERT INTO history(text) SELECT md5(generate_series::text) FROM generate_series(1,20); INSERT 0 20 postgres=> SELECT * FROM history offset 20; id | text | create_date ----+----------------------------------+---------------------------- 21 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:51:18.906937 22 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:51:18.906937 23 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:51:18.906937 24 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:51:18.906937 25 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:51:18.906937 26 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:51:18.906937 27 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:51:18.906937 28 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:51:18.906937 29 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:51:18.906937 30 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:51:18.906937 31 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:51:18.906937 32 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:51:18.906937 33 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:51:18.906937 34 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:51:18.906937 35 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:51:18.906937 36 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:51:18.906937 37 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:51:18.906937 38 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:51:18.906937 39 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:51:18.906937 40 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:51:18.906937 (20 rows) postgres=> SELECT COUNT(*) FROM history; count ------- 40 (1 row)
先ほど作成したスナップショットのARNをDBSnapshotId
に指定し同一のスタックをアップデートします。
置換条件がConditionになっているのでこの時点では発生するかは断定できません。
アップデートすると失敗してしまいました。
完全に失念していましたがこれはDBInstanceIdentifier
を指定していることで置換処理ができないためのエラーとなります。
(RDSの仕組み上同一アカウントリージョン上に同名のDBが作れない為)
識別子のために固定の名前をつけていることも多いかと思いますのでCloudFormationでDBSnapshotIdentifier
を差し替えるような型値前提としている場合はDBInstanceIdentifier
を未指定にするようにして対応しましょう。
DBInstanceIdentifier
の指定を削除したテンプレートで再作成し(DBSnapshotIdの値は空)あらためて先ほどのDBSnapshotId
を指定する形で更新を行います。
(後で冷静に考えるとわざわざ一度消さずにDBInstanceIdentifier
の指定を削除してアップデートで十分だったかもしれません)
スタックを更新してみると新しいリソースを作成するメッセージが含まれており置換処理が発生することがわかります。
作成中にDBインスタンスを見に行くと置換前後のインスタンス両方が確認できます。
SELECT文を実行して最初に取ったスナップショットの状態に戻っていることを確認します。
sh-4.2$ psql -h db-snapshot-dbinstance-yflw3q0pvlzb.xxxxxx.ap-northeast-1.rds.amazonaws.com -U postgres Password for user postgres: psql (14.8, server 15.4) WARNING: psql major version 14, server major version 15. Some psql features might not work. SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off) Type "help" for help. postgres=> select * from history; id | text | create_date ----+----------------------------------+---------------------------- 1 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:41:54.345255 2 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:41:54.345255 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:41:54.345255 4 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:41:54.345255 5 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:41:54.345255 6 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:41:54.345255 7 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:41:54.345255 8 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:41:54.345255 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:41:54.345255 10 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:41:54.345255 11 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:41:54.345255 12 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:41:54.345255 13 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:41:54.345255 14 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:41:54.345255 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:41:54.345255 16 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:41:54.345255 17 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:41:54.345255 18 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:41:54.345255 19 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:41:54.345255 20 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:41:54.345255 (20 rows)
なお今回初めて知ったのですがリストア復旧の場合はモニタリングのセッティングの後にResetting-master-credentials-message
と言う処理が挟まりこの段階でマスターユーザの情報が変更されるようです。
スナップショット指定削除
DBSnapshotId
の指定を削除しDBSnapshotIdentifier
の指定が有りから無しになった際の挙動を見ます。
この場合は特に置換が発生しないことが確認できます。
少しわかりづらいですが更新処理を行った19:32~3頃にはRDS側のイベントは特に発生しておらず、また先のスナップショットからの復元を行った際のログも残っております。
終わりに
終わりに今回はCloudFormationによるスナップショットの復元を試しつつ指定が変動した際の挙動を確認してみました。
一発目の立ち上げを行う場合には今回のような置換処理等が発生せずにスムーズに行えるため便利ではありそうですが、変更するような運用を取る際にはパラメータの指定には少し注意が必要そうです。
CloudFormationでスナップショットを利用したRDSインスタンスの立ち上げは可能ですが置換等を行う場合は条件によっては利用できないことがございますので利用シーンや差し替えのフローを整理の上、場合によっては手動での対応も視野に入れていただければと思います。